<?php
// This file is part of CorrectWriting question type - https://code.google.com/p/oasychev-moodle-plugins/
//
// CorrectWriting question type is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// CorrectWriting is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with CorrectWriting.  If not, see <http://www.gnu.org/licenses/>.


/**
 * Drawing a picture with all the changes and editions with a string
 *
 * @copyright &copy; 2011  Oleg Sychev
 * @author  Dmitriy Matyushechkin <dima_kekc@mail.ru> Volgograd State Technical University
 * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
 * @package questions
 */

defined('MOODLE_INTERNAL') || die();
define('FONT_NAME', $CFG->dirroot.'/question/type/correctwriting/TIMESET.TTF');
define('FONT_SIZE', 30);
define('LEFT_BORDER', 20);
define('BLOCK_SPACE', 34);
define('BASE_LINE', 170);
define('HIGH_BASE_LINE', BASE_LINE - 20);
define('BASE_LINE_LOW', BASE_LINE + 100);
define('DELSPACE_SHIFT', 24);



/**
  * This class contains type and parameters of each operation
  */
  abstract class qtype_correctwriting_operation_parent {

    public $operationname;
    public $lexemindex;
    public $position;
    public $placeto;
    public $lexemto;
    public $symbols;
    public $ID;
    public $coordinate;
    public $height;
    public $movestartcoord;
    public $movedestcoord;

    abstract public function get_result_string ($lexems);
    abstract public function do_work_operation ($work);
    abstract public function count_coordinates ($blocks, &$shift, $i, $h, $op);
    abstract public function draw ($img);
    
    //get place of lexem`s start
    protected function get_start_place ($index, $work) {
	    $c = 0;
		while ($work[$c]->lexem != $index)
			$c ++;

		return $c;
    }
 }

 /**
  * Class of insert operation
  */
 class qtype_correctwriting_operation_insert extends qtype_correctwriting_operation_parent {
    
    public function get_result_string ($lexems) {
        $lexems[$this->lexemindex] = substr($lexems[$this->lexemindex], 0, $this->position).$this->symbols.substr($lexems[$this->lexemindex], $this->position);
        return $lexems;
    }

    public function do_work_operation ($work) {
    	$start = $this->get_start_place($this->lexemindex, $work);
		$done = false;
		$tmp = new qtype_correctwriting_work;
	    //find a place to insert a symbol (notice a readness array element)
	    for ($i = 0; $i < $this->position; ++ $i) {
	    	//jump unreadable symbols
	    	if (! ($work[$start]->countable))
	    		$i --;
	    	$tmp = new qtype_correctwriting_work;
	    	$tmp = $work[$start];
	    	$start ++;
	    	//check the end of lexem
	    	if ($work[$start]->lexem !=$work[$start - 1]->lexem) {
	    		$tmp->posinpicture ++;
	    		$tmp->operationname = 'insert';
	    		$tmp->countable = true;
	    		$tmp->symbols = $this->symbols;
	    		array_splice($work, $start, 0, $tmp);
	    		$done = true;
	    	}
	    }
	    if ($done == false) {
	    	unset($tmp);
	    	$tmp = new qtype_correctwriting_work;
	    	$tmp->operationname = 'insert';
	    	$tmp->countable = true;
	    	$tmp->symbols = $this->symbols;
	    	$tmp->placeto = $this->position;
	    	$tmp->lexem = $work[$start]->lexem;
	    	$tmp->lexeminpicture = $work[$start]->lexeminpicture;
	    	$tmp->posinpicture = $work[$start]->posinpicture;
            $arr = array ();
            $arr[] = $tmp;
	    	array_splice($work, $start, 0, $arr);
	    }

    return $work;
    }

    public function count_coordinates ($blocks, &$shift, $i, $h, $op) {
        $tmp = $blocks[$i]->subblockhigh[$h]->symbols;
        $currentoperation = $blocks[$i]->subblockhigh[$h]->operations[$op];

        $tmp = substr($tmp, 0, $this->position + $shift);
        $textsize = imagettfbbox(FONT_SIZE, 0, FONT_NAME, $tmp);
        $width = $textsize[2] - $textsize[0];
        $height = $textsize[1] - $textsize[7];

        $this->coordinate = $blocks[$i]->subblockhigh[$h]->leftcoord + $width;
        $this->height = $height;
        $shift += strlen($this->symbols);
        return $blocks;
    }

    public function draw ($img) {
        //draw a tick
        $count = strlen($this->symbols); //length of tick in symbols
        $pointleftx = $this->coordinate - 9;
        $pointlefty = HIGH_BASE_LINE - $this->height;
        $pointmidx = $this->coordinate + (((BLOCK_SPACE - 6) * $count) / 2) - 5;
        $pointmidy = HIGH_BASE_LINE - $this->height + 15;
        $pointrightx = $this->coordinate + ((BLOCK_SPACE - 6) * $count) - 2;
        $pointrighty = $pointlefty;
        
        imageline($img, $pointleftx, $pointlefty, $pointmidx, $pointmidy, 0xFF0000);
        imageline($img, $pointmidx, $pointmidy, $pointrightx, $pointrighty, 0xFF0000);

        //draw a text
        $textsize = imagettfbbox(FONT_SIZE, 0, FONT_NAME, $this->symbols);
        $width = $textsize[2] - $textsize[0];
        $pointleftx += ((BLOCK_SPACE - 16) * $count) - $width / 2;
        $pointlefty -= 3;
        imagettftext($img, FONT_SIZE, 0, $pointleftx, $pointlefty, 0xFF0000, FONT_NAME, $this->symbols);

        return $img;
    }

 }

 /**
  * Class of insertlexem operation
  */
 class qtype_correctwriting_operation_insertlexem extends qtype_correctwriting_operation_parent {
    
    public function get_result_string ($lexems) {
        array_splice($lexems, $this->lexemindex, 0, $this->symbols);
        return $lexems;
    }
    public function do_work_operation ($work) {
    	//get index position to insert
    	$start = $this->get_start_place($this->lexemindex, $work);
    	//an array to insert
    	$insertarray = array();
    	//generate an array to insert
    	for ($i = 0; $i < strlen($this->symbols); ++ $i) {
    		$tmp = new qtype_correctwriting_work;

    		$tmp->countable = true;
   			$tmp->lexem = $this->lexemindex;
   			$tmp->lexeminpicture = $work[$start]->lexeminpicture;   			
   			$tmp->symbols = $this->symbols;
   			$tmp->ID = func_get_arg(1); 
   			//mark the first element as operation
    		if ($i == 0)
    			$tmp->operationname = 'insertlexem';
    		else
    			$tmp->operationname = 'dest';

    		$insertarray[] = $tmp;
    	}
    	//insert new array to work-array
    	array_splice($work, $start, 0, $insertarray);
    	//recount all indexes after the inserted array
    	$start = 0;
    	while ($work[$start]->lexem != $this->lexemindex)
    		$start ++;

    	$start += strlen($this->symbols);

    	while ($work[$start]->operationname != 'last') {
    		if ($work[$start]->lexem != -1)
    			$work[$start]->lexem ++;
    		$start ++;
    	}

    	$work[$start]->lexem ++;

    	return $work;
    }

    public function count_coordinates ($blocks, &$shift, $i, $h, $op) {
        if ($this->operationname == 'lowline') {
            $this->movestartcoord = $blocks[$i]->subblocklow[$h]->leftcoord;
            $this->movedestcoord = $blocks[$i]->subblocklow[$h]->leftcoord + $blocks[$i]->subblocklow[$h]->wide;
            //if lexem inserted between parts of lexem devided by space
            $this->movedestcoord -= BLOCK_SPACE;
            //if lexem in separated block
            if (count($blocks[$i]->subblocklow) == 1)
                $this->movedestcoord += BLOCK_SPACE;
        }
        return $blocks;
    }

    public function draw ($img) {
        $leftx = $this->movestartcoord;
        $lefty = BASE_LINE_LOW;
        $rightx = $this->movedestcoord + 5;
        $righty = $lefty;

        imageline($img, $leftx, $lefty, $rightx, $righty, 0xFF0000);
        return $img;
    }
 }

 /**
  * Class of delete operation
  */
 class qtype_correctwriting_operation_delete extends qtype_correctwriting_operation_parent {
    
    public function get_result_string ($lexems) {
        $lexems[$this->lexemindex] = substr($lexems[$this->lexemindex], 0, $this->position).substr($lexems[$this->lexemindex], $this->position + 1);
        return $lexems;
    }
    public function do_work_operation ($work) {
    	//get beginning of the lexem where to delete
    	$start = $this->get_start_place($this->lexemindex, $work);
    	//get position in this lexem
    	for ($i = 0; $i < $this->position; ++ $i) {
    		if (! $work[$start]->countable)
    			$i --;
    		$start ++;
    	}
    	if (!$work[$start]->countable)
    		$start ++;
    	//mark an element as deleted
    	$work[$start]->operationname = 'delete';
    	$work[$start]->countable = false;
    	return $work;
    }

    public function count_coordinates ($blocks, &$shift, $i, $h, $op) {
        //string to delete from
        $tmp = $blocks[$i]->subblockhigh[$h]->symbols;
        //get right coordinate of delete symbol
        $tmp = substr ($tmp, 0, $this->position + 1 + $shift);
        //save deleted symbol
        $char = $tmp[strlen($tmp) - 1];
        $textsize = imagettfbbox(FONT_SIZE, 0, FONT_NAME, $tmp);
        $width = $textsize[2] - $textsize[0];
        $this->movedestcoord = $blocks[$i]->subblockhigh[$h]->leftcoord + $width;
        //get left coordinate of delete symbol
        $tmp = substr ($tmp, 0, $this->position + $shift);
        $textsize = imagettfbbox(FONT_SIZE, 0, FONT_NAME, $tmp);
        $width = $textsize[2] - $textsize[0];
        $this->coordinate = $blocks[$i]->subblockhigh[$h]->leftcoord + $width;
        //get height of delete symbol
        $textsize = imagettfbbox(FONT_SIZE, 0, FONT_NAME, $char);
        $height = $textsize[1] - $textsize[7];
        //solve a type of deleting
        if ($op > 0 && $blocks[$i]->subblockhigh[$h]->operations[$op - 1]->operationname == 'delete')
            $this->ID = -1; // -1 means to delete with a line
        $opercount = count ($blocks[$i]->subblockhigh[$h]->operations);
        if ((($op + 1) != $opercount) && $blocks[$i]->subblockhigh[$h]->operations[$op + 1]->operationname == 'delete')
            $this->ID = -1;

        return $blocks;
    }

    public function draw ($img) {

        if ($this->ID == -1) {
            $delleftx = $this->coordinate;
            $dellefty = HIGH_BASE_LINE - ($this->height / 2 - 7);
            $delrightx = $this->movedestcoord;
            $delrighty = $dellefty;
            imageline($img, $delleftx, $dellefty, $delrightx, $delrighty, 0xFF0000);
        }
        else {
            $leftlowx = $this->coordinate;
            $leftlowy = BASE_LINE;
            $lefthighx = $leftlowx;
            $lefthighy = HIGH_BASE_LINE - $this->height;
            $rightlowx = $this->movedestcoord;
            $rightlowy = BASE_LINE;
            $righthighx = $this->movedestcoord;
            $righthighy = $lefthighy;

            imageline($img, $leftlowx, $leftlowy, $righthighx, $righthighy, 0xFF0000);
            imageline($img, $rightlowx, $rightlowy, $lefthighx, $lefthighy, 0xFF0000);
        }
        return $img;
    }
 }

 /**
  * Class of deletelexem operation
  */
 class qtype_correctwriting_operation_deletelexem extends qtype_correctwriting_operation_parent {
    
    public function get_result_string ($lexems) {
        array_splice($lexems, $this->lexemindex, 1);
        return $lexems;
    }
    public function do_work_operation ($work) {
    	$start = $this->get_start_place($this->lexemindex, $work);
    	$work[$start]->operationname = 'deletelexem';

    	while ($work[$start]->lexem == $this->lexemindex) {
    		$work[$start]->lexem = -1;
    		$work[$start]->countable = false;
    		$start ++;
    	}

    	while ($work[$start]->operationname != 'last') {
    		$work[$start]->lexem --;
    		$start ++;
    	}

    	$work[$start]->lexem --;

    	return $work;
    }

    public function count_coordinates ($blocks, &$shift, $i, $h, $op) {
        $this->movestartcoord = $blocks[$i]->subblockhigh[$h]->leftcoord;
        $this->movedestcoord = $blocks[$i]->subblockhigh[$h]->leftcoord + $blocks[$i]->subblockhigh[$h]->wide;

        //if lexem was connected from the right side by deletespace
        $find = 0;
        if (count($blocks[$i]->subblockhigh[$h]->operations) > ($op + 1) && $blocks[$i]->subblockhigh[$h]->operations[$op + 1]->operationname == 'deletespace')
            $this->movedestcoord += $blocks[$i]->subblockhigh[$h + 1]->wide;
        while (true) {
            $find ++;
            if (count($blocks[$i]->subblockhigh) > ($h + $find) && count($blocks[$i]->subblockhigh[$h + $find]->operations) > 0 && 
                $blocks[$i]->subblockhigh[$h + $find]->operations[0]->operationname == 'deletespace')
                $this->movedestcoord += $blocks[$i]->subblockhigh[$h + $find + 1]->wide;
            else
                break;
        }
        return $blocks;
    }

    public function draw ($img) {
        $leftx = $this->movestartcoord;
        $lefty = HIGH_BASE_LINE + 7;
        $rightx = $this->movedestcoord;
        $righty = $lefty;

        imageline($img, $leftx, $lefty, $rightx, $righty, 0xFF0000);
        return $img;
    }
 }

 /**
  * Class of move operation
  */
 class qtype_correctwriting_operation_move extends qtype_correctwriting_operation_parent {
    
    public function get_result_string ($lexems) {
        //check a side of movement
            if ($this->placeto > $this->position)
                $this->placeto -= 1;
            //save a movable symbol
            $tmp = $lexems[$this->lexemindex][$this->position];
            //delete movable symbol from one place
            $lexems[$this->lexemindex] = substr($lexems[$this->lexemindex], 0, $this->position).substr($lexems[$this->lexemindex], $this->position + 1);
            //insert movable to another place
            $lexems[$this->lexemindex] = substr($lexems[$this->lexemindex], 0, $this->placeto).$tmp.substr($lexems[$this->lexemindex], $this->placeto);

            if ($this->placeto > $this->position)
                $this->placeto += 1;
        return $lexems;
    }
    public function do_work_operation ($work) {
    	$start = $this->get_start_place($this->lexemindex, $work);
        $it = $start;

    	$tmp = new qtype_correctwriting_work;
    	$done = false;
    	for ($i = 0; $i < $this->position; ++ $i) {
    		if (! $work[$it]->countable)
    			$i --;
    		$it ++;
    	}
    	if (! $work[$it]->countable)
    		$it ++;

    	$work[$it]->operationname = 'move';
    	$work[$it]->ID = func_get_arg(1);
    	$work[$it]->countable = true;

    	$from = $it;

    	for ($i = 0; $i < $this->placeto; ++ $i) {
    		if (! $work[$start]->countable)
    			$i --;
            
            $tmp = clone $work[$start];
    		$start ++;

    		if ($tmp->lexem != $work[$start]->lexem) {
    			$work[$from]->countable = false;
    			$work[$from]->lexemto = $work[$start]->lexeminpicture;
    			$tmp->posinpicture ++;
                $work[$from]->placeto = $tmp->posinpicture;
                $insertarray = array ();
                $insertarray[] = $tmp;
    			array_splice($work, $start, 0, $insertarray);
    			$done = true;
    		}
    	}

    	if (! $done) {
    		$work[$from]->countable = false;
    		$work[$from]->placeto = $work[$start]->posinpicture;
    		$work[$from]->lexemto = $work[$start]->lexeminpicture;

    		$tmp = clone $work[$start];

    		$tmp->operationname = 'dest';
    		$tmp->countable = true;
    		$tmp->ID = func_get_arg(1);
    		$insertarray = array ();
    		$insertarray[] = $tmp;

    		array_splice($work, $start, 0, $insertarray);
    	}

    	return $work;
    }

    public function count_coordinates ($blocks, &$shift, $i, $h, $op) {
        if ($this->operationname == 'movestart') {
            //find coordinates of arrow`s beginning
            $tmp = $blocks[$i]->subblockhigh[$h]->symbols;
            $tmp = substr($tmp, 0, $this->position + $shift);
            $textsize = imagettfbbox(FONT_SIZE, 0, FONT_NAME, $tmp);
            $width = $textsize[2] - $textsize[0];
            $this->movestartcoord = $blocks[$i]->subblockhigh[$h]->leftcoord + $width;
            $height = $textsize[1] - $textsize[7];
            $this->height = $height;
            //find coordinates of arrow`s end
            $ID = $this->ID;
            $blockscount = count($blocks);
            //for each block
            for ($b = 0; $b < $blockscount; ++ $b) {
                $subcount = count($blocks[$b]->subblockhigh);
                //for each subblock
                for ($s = 0; $s < $subcount; ++ $s) {
                    $opercount = count($blocks[$b]->subblockhigh[$s]->operations);
                    //for each operation
                    $shiftlocal = 0;
                    for ($o = 0; $o < $opercount; ++ $o) {
                        if ($blocks[$b]->subblockhigh[$s]->operations[$o]->operationname == 'insert') {
                            $shiftlocal += strlen($blocks[$b]->subblockhigh[$s]->operations[$o]->symbols);
                            $this->coordinate = -1; //mark unusable parameter that insert is here
                        }
                        //find position for inserting and find its coordinates
                        if ($blocks[$b]->subblockhigh[$s]->operations[$o]->operationname == 'movedest' && $blocks[$b]->subblockhigh[$s]->operations[$o]->ID == $ID) {
                            $tmp = $blocks[$b]->subblockhigh[$s]->symbols;
                            $tmp = substr($tmp, 0, $blocks[$b]->subblockhigh[$s]->operations[$o]->position + $shiftlocal);
                            $textsize = imagettfbbox(FONT_SIZE, 0, FONT_NAME, $tmp);
                            $width = $textsize[2] - $textsize[0];
                            $this->movedestcoord = $blocks[$b]->subblockhigh[$s]->leftcoord + $width;
                        }
                    }
                }
            }
        }
        return $blocks;
    }

    public function draw ($img) {
        if($this->operationname == 'movestart') {
            $startx = $this->movestartcoord;
            $starty = BASE_LINE - 27;
            $destx = $this->movedestcoord;
            $desty = BASE_LINE - 29;
            //draw an arc between two points
            //if arrow goes from right to left
            if ($this->movedestcoord < $this->movestartcoord) {
                $axewidth = abs($this->movedestcoord - $this->movestartcoord) + 14;
                $arrowleftx = $this->movedestcoord - 4;
                $arrowlefty = BASE_LINE - 35;
                $arrowrightx = $this->movedestcoord + 4;
                $arrowrighty = BASE_LINE - 35;
            }
            else {
                $axewidth = abs($this->movedestcoord - $this->movestartcoord) - 10;
                $arrowleftx = $this->movedestcoord - 5;
                $arrowlefty = BASE_LINE - 36;
                $arrowrightx = $this->movedestcoord + 3;
                $arrowrighty = BASE_LINE - 37;
            }

            //draw an arc
            $axeheight = 80 + (13 * $this->ID);//make different arrows for one lexem
            //if width of arc too large 
            if (abs($this->movestartcoord - $this->movedestcoord) > 80 || $this->coordinate == -1)
                $axeheight += 50;
            $centerx = $this->movestartcoord + ($this->movedestcoord - $this->movestartcoord) / 2 + 6;
            $centery = BASE_LINE - 30;
            imagearc($img, $centerx, $centery, $axewidth, $axeheight, 180, 0, 0xFF0000);
            //draw ends of arrow
            imageline($img, $destx, $desty, $arrowleftx, $arrowlefty, 0xFF0000);
            imageline($img, $destx, $desty, $arrowrightx, $arrowrighty, 0xFF0000);
        }

        return $img;
    }
 }

 /**
  * Class of movelexem operation
  */
 class qtype_correctwriting_operation_movelexem extends qtype_correctwriting_operation_parent {
    
    public function get_result_string ($lexems) {
    	$this->symbols = $lexems[$this->position];
        //check a side of movement
        $placetoinsert = $this->placeto;
        if ($this->placeto > $this->position)
            $placetoinsert --;
            //save a movable lexem
            $tmp = $lexems[$this->position];
            //erase movable lexem from one place
            array_splice($lexems, $this->position, 1);
            //insert movable lexem to another place
            array_splice($lexems, $placetoinsert, 0, $tmp);
        return $lexems;
    }
    public function do_work_operation ($work) {
    	$movedlexem = $this->symbols;
    	$this->symbols = $movedlexem;
    	$ins = new qtype_correctwriting_operation_insertlexem;
    	$ins->operationname = 'insertlexem';
    	$ins->lexemindex = $this->placeto;
    	$ins->symbols = $this->symbols;
    	if ($this->position < $this->placeto)
    		$work = $ins->do_work_operation($work, func_get_arg(1));

    	$a = $this->placeto;
    	//save to ins var index of arrow`s end
    	$start = $this->get_start_place($this->placeto, $work);
    	$ins->placeto = $work[$start]->lexeminpicture;
    	//get to moving position in array
    	$start = $this->get_start_place($this->position, $work);
    	//set -1 as index for lexem and mark as deleted
    	$work[$start]->operationname = 'movelexem';
    	$work[$start]->ID = func_get_arg(1);
    	//save an index of arrow`s end
    	$work[$start]->lexemto = $ins->placeto;
    	while ($work[$start]->lexem == $this->position) {
    		$work[$start]->lexem = -1;
    		$work[$start]->countable = false;
    		$start ++;
    	}

    	//recount indexes since deleted lexem
    	while ($work[$start]->operationname != 'last') {
    		$work[$start]->lexem --;
    		$start ++;
    	}
    	$work[$start]->lexem --;

    	if ($this->position >= $this->placeto)
    		$work = $ins->do_work_operation($work, func_get_arg(1));
    	return $work;
    }

    public function count_coordinates ($blocks, &$shift, $i, $h, $op) {
        if ($this->operationname == 'movelexemstart') {
            $this->movestartcoord = $blocks[$i]->subblockhigh[$h]->leftcoord;
            $this->movedestcoord = $blocks[$i]->subblockhigh[$h]->leftcoord + $blocks[$i]->subblockhigh[$h]->wide;

            //find coordinates of arrow`s end
            $ID = $this->ID;
            $blockscount = count($blocks);
            for ($b = 0; $b < $blockscount; ++ $b) {
                $subcount = count($blocks[$b]->subblocklow);
                for ($j = 0; $j < $subcount; ++ $j) {
                    $opercount = count($blocks[$b]->subblocklow[$j]->operations);
                    for ($k = 0; $k < $opercount; ++$k) {
                        if ($blocks[$b]->subblocklow[$j]->operations[$k]->ID == $ID) {
                            $this->coordinate = $blocks[$b]->subblocklow[$j]->leftcoord;
                            $this->height = $blocks[$b]->subblocklow[$j]->wide;
                        }
                    }
                }
            }
            // if there are lexems which connected by deleting space
            $find = 0;
            if (count($blocks[$i]->subblockhigh[$h]->operations) > ($op + 1) && $blocks[$i]->subblockhigh[$h]->operations[$op + 1]->operationname == 'deletespace')
                $this->movedestcoord += $blocks[$i]->subblockhigh[$h + 1]->wide;
            else
                return $blocks;
            while (true) {
                $find ++;
                if (count($blocks[$i]->subblockhigh) > ($h + $find) && count($blocks[$i]->subblockhigh[$h + $find]->operations) > 0 && 
                    $blocks[$i]->subblockhigh[$h + $find]->operations[0]->operationname == 'deletespace')
                    $this->movedestcoord += $blocks[$i]->subblockhigh[$h + $find + 1]->wide;
                else
                    break;
            }
        }
        return $blocks;
    }

    public function draw ($img) {
        $ex = 2; //if exchange was used above lexem
        if ($this->lexemto == (-1))
            $ex += 20;

        $leftx = $this->movestartcoord;
        $lefty = BASE_LINE + $ex;
        $rightx = $this->movedestcoord;
        $righty = $lefty;
        $midx = $this->movestartcoord + ($rightx - $leftx) / 2;
        $midy = $lefty;
        //if arrow shows from left to right
        $dest1x = $this->coordinate;
        $dest1y = BASE_LINE_LOW - 32; //coordinate of left border of the showed lexem
        //from right to the left 
        $dest2x = $this->coordinate + $this->height - 18; //height here means width
        $dest2y = $dest1y;
        //line under the moved lexem
        imageline($img, $leftx, $lefty, $rightx, $righty, 0xFF0000);

        if ($midx < $this->coordinate) {
            imageline($img, $midx, $midy, $dest1x, $dest1y, 0xFF0000);
            $destx = $dest1x;
            $desty = $dest1y;
        }
        else {
            imageline($img, $midx, $midy, $dest2x, $dest2y, 0xFF0000);
            $destx = $dest2x;
            $desty = $dest2y;
        }
        //end of the arrow
        $pi = 3.141592;
        $r = 15;
        $y = $midy - $desty;
        $x = $midx - $destx;
        $alpha = atan2($y, $x);
        $alphar = $alpha + $pi / 6;
        $alphal = $alpha - $pi / 6;
        $arrow1x = $destx + $r * cos($alphar);
        $arrow1y = $desty + $r * sin($alphar);
        $arrow2x = $destx + $r * cos($alphal);
        $arrow2y = $desty + $r * sin($alphal);

        imageline($img, $destx, $desty, $arrow1x, $arrow1y, 0xFF0000);
        imageline($img, $destx, $desty, $arrow2x, $arrow2y, 0xFF0000);
        return $img;
    }
 }

 /**
  * Class of space operation
  */
 class qtype_correctwriting_operation_space extends qtype_correctwriting_operation_parent {
    
    public function get_result_string ($lexems) {
        //copy a lexem to separate
            $tmp = $lexems[$this->lexemindex];
            //take left part of lexem
            $lexems[$this->lexemindex] = substr($lexems[$this->lexemindex], 0, $this->position);
            //take right part of lexem
            $tmp = substr($tmp, $this->position);
            //put parts to different places of array
            array_splice($lexems, $this->lexemindex + 1, 0, $tmp);
        return $lexems;
    }
    public function do_work_operation ($work) {
    	$tmp = new qtype_correctwriting_work;
    	$start = $this->get_start_place($this->lexemindex, $work);

    	for ($i = 0; $i < $this->position; ++ $i) {
    		if (! $work[$start]->countable)
    			$i --;
    		$start ++;
    	}
    	if (! $work[$start]->countable)
    		$start ++;

    	$ins = $start;
    	$tmp = clone $work[$start];
    	$tmp->operationname = 'space';
    	$tmp->countable = false;

    	while ($work[$start]->operationname != 'last') {
    		if ($work[$start]->lexem != -1)
    			$work[$start]->lexem ++;
    		$start ++;
    	}
    	$work[$start]->lexem ++;

    	$insertarray = array();
    	$insertarray[] = $tmp;
    	array_splice($work, $ins, 0, $insertarray);

    	return $work;
    }

    public function count_coordinates ($blocks, &$shift, $i, $h, $op) {
        $this->coordinate = $blocks[$i]->subblockhigh[$h]->leftcoord + $blocks[$i]->subblockhigh[$h]->wide;
        return $blocks;
    }

    public function draw ($img) {
        $lowx = $this->coordinate;
        $lowy = BASE_LINE + 3;
        $highx = $lowx;
        $highy = BASE_LINE - 36;
        $leftx = $this->coordinate - 7;
        $lefty = $lowy;
        $rightx = $this->coordinate + 7;
        $righty = $highy;

        imageline($img, $lowx, $lowy, $highx, $highy, 0xFF0000);
        imageline($img, $leftx, $lefty, $lowx, $lowy, 0xFF0000);
        imageline($img, $rightx, $righty, $highx, $highy, 0xFF0000);
        return $img;
    }
 }

 /**
  * Class of change operation
  */
 class qtype_correctwriting_operation_change extends qtype_correctwriting_operation_parent {
    
    public function get_result_string ($lexems) {
        $lexems[$this->lexemindex] = substr_replace($lexems[$this->lexemindex], $this->symbols, $this->position, 1);
        return $lexems;
    }
    public function do_work_operation ($work) {
    	$start = $this->get_start_place($this->lexemindex, $work);

    	for ($i = 0; $i < $this->position; ++ $i) {
    		if (! $work[$start]->countable)
    			$i --;
    		$start ++;
    	}

    	$work[$start]->operationname = 'change';
    	$work[$start]->symbols = $this->symbols;
    	$work[$start]->placeto = $this->position;
    	$work[$start]->posinpicture = $this->placeto;

    	return $work;
    }

    public function count_coordinates ($blocks, &$shift, $i, $h, $op) {
        $tmp = $blocks[$i]->subblocklow[$h]->symbols;
        $tmp = substr($tmp, 0, $this->position);
        $textsize = imagettfbbox(FONT_SIZE, 0, FONT_NAME, $tmp);
        $width = $textsize[2] - $textsize[0];
        $this->coordinate = $blocks[$i]->subblocklow[$h]->leftcoord + $width;
        $tmp = $blocks[$i]->subblocklow[$h]->symbols[$this->position];
        $this->symbols = $tmp;
        return $blocks;
    }

    public function draw ($img) {
        $letterx = $this->coordinate + 1;
        $lettery = BASE_LINE_LOW;
        imagettftext($img, FONT_SIZE + 2, 0, $letterx, $lettery, 0xFF0000, FONT_NAME, $this->symbols);
        return $img;
    }
 }

 /**
  * Class of changelexem operation
  */
 class qtype_correctwriting_operation_changelexem extends qtype_correctwriting_operation_parent {
    
    public function get_result_string ($lexems) {
        $lexems[$this->lexemindex] = $this->symbols;
        return $lexems;
    }
    public function do_work_operation ($work) {
    	$start = $this->get_start_place($this->lexemindex, $work);

    	$work[$start]->operationname = 'changelexem';
    	$work[$start]->symbols = $this->symbols;
    	return $work;
    }

    public function count_coordinates ($blocks, &$shift, $i, $h, $op) {
        if ($this->operationname == 'lowline') {
            $this->movestartcoord = $blocks[$i]->subblocklow[$h]->leftcoord;
            $this->movedestcoord = $blocks[$i]->subblocklow[$h]->leftcoord + $blocks[$i]->subblocklow[$h]->wide;
            //if lexem inserted between parts of lexem devided by space
            $this->movedestcoord -= BLOCK_SPACE;
            //if lexem in separated block
            if (count($blocks[$i]->subblocklow) == 1)
                $this->movedestcoord += BLOCK_SPACE;
        }
        return $blocks;
    }

    public function draw ($img) {
        return $img;
    }
 }

 /**
  * Class of deletespace operation
  */
 class qtype_correctwriting_operation_deletespace extends qtype_correctwriting_operation_parent {
    
    public function get_result_string ($lexems) {
        $lexems[$this->lexemindex - 1] .= $lexems[$this->lexemindex];
            unset($lexems[$this->lexemindex]);
            sort($lexems);
        return $lexems;
    }
    public function do_work_operation ($work) {
    	$start = $this->get_start_place($this->lexemindex, $work);
    	$work[$start]->operationname = 'deletespace';

    	while ($work[$start]->operationname != 'last') {
    		$work[$start]->lexem --;
    		$start ++;
    	}
    	$work[$start]->lexem --;
    	return $work;
    }

    public function count_coordinates ($blocks, &$shift, $i, $h, $op) {
        $this->coordinate = $blocks[$i]->subblockhigh[$h]->leftcoord + $blocks[$i]->subblockhigh[$h]->wide - DELSPACE_SHIFT;
        $this->movedestcoord = $blocks[$i]->subblockhigh[$h + 1]->leftcoord + 10;
        return $blocks;
    }

    public function draw ($img) {
        $ax = $this->coordinate - 15;
        $ay = BASE_LINE;
        $bx = $this->movedestcoord - 5;
        $by = $ay;
        imageline($img, $ax, $ay, $bx, $by, 0xFF0000);
        return $img;
    }
 }

 /**
  * Class of exchange operation
  */
 class qtype_correctwriting_operation_exchange extends qtype_correctwriting_operation_parent {
    
    public function get_result_string ($lexems) {
        $tmp = $lexems[$this->lexemindex][$this->position + 1];
            $lexems[$this->lexemindex][$this->position + 1] = $lexems[$this->lexemindex][$this->position];
            $lexems[$this->lexemindex][$this->position] = $tmp;
        return $lexems;
    }
    public function do_work_operation ($work) {
    	$start = $this->get_start_place($this->lexemindex, $work);

    	for ($i = 0; $i < $this->position; ++ $i) {
    		if (! $work[$start]->countable)
    			$i --;
    		$start ++;
    	}
    	if (! $work[$start]->countable)
    		$start ++;

    	$work[$start]->operationname = 'exchange';

    	return $work;
    }

    public function count_coordinates ($blocks, &$shift, $i, $h, $op) {
        //check if this lexem was moved
        for ($check = 0; $check < count($blocks[$i]->subblockhigh[$h]->operations); ++ $check) {
            if ($blocks[$i]->subblockhigh[$h]->operations[$check]->operationname == 'movelexemstart')
                $blocks[$i]->subblockhigh[$h]->operations[$check]->lexemto = -1;
        }
        $tmp = $blocks[$i]->subblockhigh[$h]->symbols;
        $tmp = substr($tmp, 0, $this->position + $shift);
        $textsize = imagettfbbox(FONT_SIZE, 0, FONT_NAME, $tmp);
        $width = $textsize[2] - $textsize[0];
        $height = $textsize[1] - $textsize[7];
        $this->coordinate = $blocks[$i]->subblockhigh[$h]->leftcoord + $width;
        $this->height = $height;

        return $blocks;
    }

    public function draw ($img) {
        //draw arcs
        $centerx = $this->coordinate + 20;
        $centery = HIGH_BASE_LINE - 13;
        $axewidth = 21;
        $axeheight = 20;
        imagearc($img, $centerx, $centery, $axewidth, $axeheight, 180, 0, 0xFF0000);

        $centerx = $this->coordinate + 20;
        $centery = BASE_LINE + 7; 
        $axewidth = 21;
        $axeheight = 20;
        imagearc($img, $centerx, $centery, $axewidth, $axeheight, 0, 180, 0xFF0000);

        //draw arrows
        $midx = $this->coordinate + 9;
        $midy = HIGH_BASE_LINE - 11;
        $arrowleftx = $this->coordinate + 7;
        $arrowlefty = HIGH_BASE_LINE - 17;
        imageline($img, $midx, $midy, $arrowleftx, $arrowlefty, 0xFF0000);
        $arrowrightx = $this->coordinate + 14;
        $arrowrighty = $arrowlefty;
        imageline($img, $midx, $midy, $arrowrightx, $arrowrighty, 0xFF0000);

        $midx = $this->coordinate + 29;
        $midy = BASE_LINE + 4;
        $arrowleftx = $midx - 3;
        $arrowlefty = $midy + 6;
        imageline($img, $midx, $midy, $arrowleftx, $arrowlefty, 0xFF0000);
        $arrowrightx = $arrowleftx + 5;
        $arrowrighty = $arrowlefty;
        imageline($img, $midx, $midy, $arrowrightx, $arrowrighty, 0xFF0000);
        return $img;
    }
 }

 /**
  * This class contains fields of work array elements for recount_indexes function
  */
 class qtype_correctwriting_work {

 	public $lexem;
 	public $lexeminpicture;
 	public $posinpicture;
 	public $operationname;
 	public $placeto;
 	public $lexemto;
 	public $symbols;
 	public $countable;
 	public $ID; 
 }

 /**
  * This class contains fields about subblocks of the block
  */
 class qtype_correctwriting_subblock {

 	public $symbols;
 	public $leftcoord;
 	public $wide;
 	public $operations;

    public function draw ($image, $high) {
        if ($high) 
            imagettftext($image, FONT_SIZE, 0, $this->leftcoord, BASE_LINE, 0x000000, FONT_NAME, $this->symbols);
        else
            imagettftext($image, FONT_SIZE, 0, $this->leftcoord, BASE_LINE_LOW, 0x000000, FONT_NAME, $this->symbols);
        return $image;
    } 
 }

 /**
  * This class contains fields about subblocks of the block
  */
 class qtype_correctwriting_block {

 	public $subblockhigh;
 	public $subblocklow;
 	public $leftcoord;
 	public $wide;

    public function draw ($image) {
        foreach ($this->subblockhigh as $high) {
            $high->draw($image, true);
        }
        foreach ($this->subblocklow as $low) {
            $low->draw($image, false);
        }
    } 
 }

//A function to get objects of operations from strings
 function qtype_correctwriting_get_objects ($stringoperations) {

    $opercount = count ($stringoperations);
               
    for ($i = 0; $i < $opercount; ++ $i) {

        $end = strpos($stringoperations[$i], ':', 0 );
        $currentoperation = substr($stringoperations[$i], 0, $end );

        if ($currentoperation == 'insert') {

            $lexemindex = substr($stringoperations[$i], 7, strpos($stringoperations[$i], ':', 7) - 7);    
            $position = substr($stringoperations[$i], 8 + strlen($lexemindex), strrpos($stringoperations[$i], ':') - strlen($stringoperations[$i]) );
            $symbols = substr($stringoperations[$i], strripos($stringoperations[$i], ':') + 1);

            $newobject = new qtype_correctwriting_operation_insert;
            $newobject->operationname = 'insert';
            $newobject->lexemindex = $lexemindex;
            $newobject->position = $position;
            $newobject->symbols = $symbols;
            $objectoperations[] = $newobject;            
        }
        elseif ($currentoperation == 'insertlexem') {
            $lexemindex = substr($stringoperations[$i], 12, strpos($stringoperations[$i], ':', 12) - 12);
            $symbols = substr($stringoperations[$i], strrpos($stringoperations[$i], ':') + 1);

            $newobject = new qtype_correctwriting_operation_insertlexem;
            $newobject->operationname = 'insertlexem';
            $newobject->lexemindex = $lexemindex;
            $newobject->symbols = $symbols;
            $objectoperations[] = $newobject;            
        }
        elseif ($currentoperation == 'delete') {
            $lexemindex = substr($stringoperations[$i], 7, strpos($stringoperations[$i], ':', 7) - 7);
            $position = substr($stringoperations[$i], strrpos($stringoperations[$i], ':') + 1 );

            $newobject = new qtype_correctwriting_operation_delete;
            $newobject->operationname = 'delete';
            $newobject->lexemindex = $lexemindex;
            $newobject->position = $position;
            $objectoperations[] = $newobject;
        }
        elseif ($currentoperation == 'deletelexem') {
            $lexemindex = substr($stringoperations[$i], strrpos($stringoperations[$i], ':') + 1 );

            $newobject = new qtype_correctwriting_operation_deletelexem;
            $newobject->operationname = 'deletelexem';
            $newobject->lexemindex = $lexemindex;
            $objectoperations[] = $newobject;
        }
        elseif ($currentoperation == 'move') {
            $lexemindex = substr($stringoperations[$i], 5, strpos($stringoperations[$i], ':', 5) - 5 );
            $position = substr($stringoperations[$i], 6 + strlen($lexemindex), strrpos($stringoperations[$i], ':') - strlen($stringoperations[$i]));
            $placeto = substr($stringoperations[$i], strrpos($stringoperations[$i], ':') + 1);            
            
            $newobject = new qtype_correctwriting_operation_move;
            $newobject->operationname = 'move';
            $newobject->lexemindex = $lexemindex;
            $newobject->position = $position;
            $newobject->placeto = $placeto;
            $objectoperations[] = $newobject;
        }
        elseif ($currentoperation == 'movelexem') {
            $position = substr($stringoperations[$i], 10, strpos($stringoperations[$i], ':', 10) - 10 );
            $placeto = substr($stringoperations[$i], strrpos($stringoperations[$i], ':') + 1 );

            $newobject = new qtype_correctwriting_operation_movelexem;
            $newobject->operationname = 'movelexem';
            $newobject->position = $position;
            $newobject->placeto = $placeto;
            $objectoperations[] = $newobject;
        }
        elseif ($currentoperation == 'space') {
            $lexemindex = substr($stringoperations[$i], 6, strpos($stringoperations[$i], ':', 6) - 6 );
            $position = substr($stringoperations[$i], strrpos($stringoperations[$i], ':') + 1);

            $newobject = new qtype_correctwriting_operation_space;
            $newobject->operationname = 'space';
            $newobject->lexemindex = $lexemindex;
            $newobject->position = $position;
            $objectoperations[] = $newobject;
        }
        elseif ($currentoperation == 'change') {
            $lexemindex = substr($stringoperations[$i], 7, strpos($stringoperations[$i], ':', 7) - 7);
            $position = substr($stringoperations[$i], 8 + strlen($lexemindex), strpos($stringoperations[$i], ':', 7 + strlen($lexemindex)) - 8 + strlen($lexemindex));
            $symbols = substr($stringoperations[$i], strrpos($stringoperations[$i], ':') + 1 );

            $newobject = new qtype_correctwriting_operation_change;
            $newobject->operationname = 'change';
            $newobject->lexemindex = $lexemindex;
            $newobject->position = $position;
            $newobject->symbols = $symbols;
            $objectoperations[] = $newobject;
        }
        elseif ($currentoperation == 'changelexem') {
            $lexemindex = substr($stringoperations[$i], 12, strpos($stringoperations[$i], ':', 12) - 12);
            $symbols = substr($stringoperations[$i], strrpos($stringoperations[$i], ':') + 1);

            $newobject = new qtype_correctwriting_operation_changelexem;
            $newobject->operationname = 'changelexem';
            $newobject->lexemindex = $lexemindex;
            $newobject->symbols = $symbols;
            $objectoperations[] = $newobject;
        }
        elseif ($currentoperation == 'deletespace') {
            $lexemindex = substr($stringoperations[$i], strrpos($stringoperations[$i], ':') + 1);

            $newobject = new qtype_correctwriting_operation_deletespace;
            $newobject->operationname = 'deletespace';
            $newobject->lexemindex = $lexemindex;
            $objectoperations[] = $newobject;
        }
        elseif ($currentoperation == 'exchange') {
            $lexemindex = substr($stringoperations[$i], 9, strpos($stringoperations[$i], ':', 9) - 9);
            $position = substr($stringoperations[$i], strrpos($stringoperations[$i], ':') + 1);

            $newobject = new qtype_correctwriting_operation_exchange;
            $newobject->operationname = 'exchange';
            $newobject->lexemindex = $lexemindex;
            $newobject->position = $position;
            $objectoperations[] = $newobject;
        }

    }

    if ($stringoperations[0] == '')
        $objectoperations = 'no operation';
        
    return $objectoperations;
 }

//A function to get result string from the source string and operations
function qtype_correctwriting_get_result_string ($lexems, $objectoperations) {

    if ($objectoperations == 'no operation')
        return $lexems;
    
    $opercount = count($objectoperations);

    for ($i = 0; $i < $opercount; ++ $i) 
        $lexems = $objectoperations[$i]->get_result_string($lexems);
    
    return $lexems;
}


//function to recount all indexes from input data to picture
function qtype_correctwriting_recount_indexes ($objectoperations, $lexems) {
	//check if lexems array is empty
	if (count($lexems) == 1 && strlen($lexems[0]) == 0)
		$lexems[0] = ' ';
	//create default array for lexems without operations
	$lexemscount = count($lexems);
	//independent variable from loops for counting all letters
	$letters = 0;
	for ($i = 0; $i < $lexemscount; ++ $i) {
		for ($j = 0; $j < strlen($lexems[$i]); ++ $j) {
			$tmp = new qtype_correctwriting_work;
			$work[] = $tmp;
			$work[$letters]->lexem = $i;
			$work[$letters]->lexeminpicture = $i;
			$work[$letters]->posinpicture = $j;
			$work[$letters]->countable = true;
			$letters ++;
		}
	}
    //add last element of array
	$tmp = new qtype_correctwriting_work;
	$tmp->operationname = 'last';
	$tmp->countable = true;
	$tmp->lexem = $lexemscount;
	$tmp->lexeminpicture = -5;
	$work[] = $tmp;

	//ID for start and end of each arrow on the picture
	$ID = 0;

    if ($objectoperations == 'no operation')
        return $work;

	$opercount = count ($objectoperations);
	for ($i = 0; $i < $opercount; ++ $i) {
		//set ID for move and movelexem operation
		if ($objectoperations[$i]->operationname == 'move') {
			$ID ++;
			$work = $objectoperations[$i]->do_work_operation($work, $ID);
		}
		elseif ($objectoperations[$i]->operationname == 'movelexem') {
			$ID ++;
			$work = $objectoperations[$i]->do_work_operation($work, $ID);
		}
		elseif ($objectoperations[$i]->operationname == 'change') {
			for ($j = $i; $j < count($objectoperations); ++ $j) {
				//set parameters of each operation depended of operations order
				//delete
				if ($objectoperations[$j]->operationname == 'delete' && $objectoperations[$j]->position < $objectoperations[$i]->position)
					$objectoperations[$i]->placeto = $objectoperations[$i]->position - 1;
				else
					$objectoperations[$i]->placeto = $objectoperations[$i]->position;
				//insert
				if ($objectoperations[$j]->operationname == 'insert' && $objectoperations[$j]->position < $objectoperations[$i]->position)
					$objectoperations[$i]->placeto = $objectoperations[$i]->position + 1;
				//move
				if ($objectoperations[$j]->operationname == 'move' && $objectoperations[$j]->placeto < $objectoperations[$i]->position)
					$objectoperations[$i]->placeto ++;
				if ($objectoperations[$j]->operationname == 'move' && $objectoperations[$j]->position < $objectoperations[$i]->position)
					$objectoperations[$i]->placeto --;
			}
			$work = $objectoperations[$i]->do_work_operation($work);
		}
		else
			$work = $objectoperations[$i]->do_work_operation($work, -1);
	}

	return $work;
}

//A function to get objects of operations from strings
function qtype_correctwriting_generate_blocks ($work, $sourcestring, $resultstring) {

 	$blocks = array ();
 	$tmp = new qtype_correctwriting_block;
 	$lowtmp = new qtype_correctwriting_subblock;
 	$hightmp = new qtype_correctwriting_subblock;
 	//set counters for loops
 	$i = 0;
 	$j = 0;
 	$lettercounttype1 = 0;
 	$lettercounttype2 = 0;
 	$lettercountsubblock = 0;
 	$picturelexem = 0;
 	$lexem = 0;
 	//set flags
 	$type1 = false;
 	$type2 = false;
 	$wasgeneratedbytype2 = false;
 	$f =0;
 	//analyze each element of work lexem
 	while ($work[$i]->operationname != 'last') {
 		$wasgeneratedbytype2 = false;
 		//remember an index of current lexem on the picture
 		$picturelexem = $work[$i]->lexeminpicture;
 		//write current lexem (notice operation)
 		$lexem = $work[$i]->lexem;

 		//save all the figures (which symbols are taken from operations) which are in the same lexem on the picture
 		while ($work[$i]->lexeminpicture == $picturelexem && $work[$i]->operationname != 'last') {
 			$lexem = $work[$i]->lexem;
 			//situations when high and low subblocks are created
 			while ($lexem == $work[$i]->lexem && $work[$i]->lexem >= 0 && $work[$i]->operationname != 'insertlexem' && $work[$i]->operationname != 'last') {
	 			//mark that this loop was entered
	 			$type1 = true;
	 			//ignore inserted symbols which are not in the sourse string
	 			if ($work[$i]->operationname == 'space' || $work[$i]->operationname == 'insert' || $work[$i]->operationname == 'dest' ) {
	 
	 				if ($work[$i]->operationname == 'space') {
	 					$figtmp = new qtype_correctwriting_operation_space;
	 					$figtmp->operationname = 'space';
	 					$hightmp->operations[] = $figtmp;
	 					$i ++;
	 				}
	 				elseif ($work[$i]->operationname == 'insert') {
	 					$figtmp = new qtype_correctwriting_operation_insert;
	 					$figtmp->operationname = 'insert';
	 					$figtmp->position = $lettercountsubblock;
	 					while ($work[$i]->operationname == 'insert') {
	 						$figtmp->symbols .= $work[$i]->symbols;
	 						$hightmp->symbols.= '  ';
	 						$i ++;	
	 					}
	 					$hightmp->operations[] = $figtmp;
	 				}
	 				elseif ($work[$i]->operationname == 'dest') {
	 					$figtmp = new qtype_correctwriting_operation_move;
	 					$figtmp->operationname = 'movedest';
	 					$figtmp->ID = $work[$i]->ID;
	 					$figtmp->position = $lettercountsubblock;
	 					$hightmp->operations[] = $figtmp;
	 					$i ++;
	 				}
	 			}
	 			else {

	 				if ($work[$i]->operationname == 'delete') {
	 					$figtmp = new qtype_correctwriting_operation_delete;
	 					$figtmp->operationname = 'delete';
	 					$figtmp->position = $lettercountsubblock;
	 					$hightmp->operations[] = $figtmp;
	 				} 
	 				elseif ($work[$i]->operationname == 'exchange') {
	 					$figtmp = new qtype_correctwriting_operation_exchange;
	 					$figtmp->operationname = 'exchange';
	 					$figtmp->position = $lettercountsubblock;
	 					$hightmp->operations[] = $figtmp;
	 				}
	 				elseif ($work[$i]->operationname == 'deletespace') {
	 					$figtmp = new qtype_correctwriting_operation_deletespace;
	 					$figtmp->operationname = 'deletespace';
	 					$figtmp->movestartcoord = -3;
	 					$hightmp->operations[] = $figtmp;
	 					$lettercounttype1 = 0;
	 					$tmp->subblockhigh[] = $hightmp;
	 					$lettercountsubblock = 0;
	 					$hightmp = new qtype_correctwriting_subblock;
	 				}
	 				elseif ($work[$i]->operationname == 'move') {
	 					$figtmp = new qtype_correctwriting_operation_move;
	 					$figtmp->operationname = 'movestart';
	 					$figtmp->ID = $work[$i]->ID;
	 					$figtmp->position = $lettercountsubblock;
	 					$hightmp->operations[] = $figtmp;
	 				}
	 				elseif ($work[$i]->operationname == 'change') {
	 					$figtmp = new qtype_correctwriting_operation_delete;
	 					$figtmp->operationname = 'delete';
	 					$figtmp->position = $lettercountsubblock;
	 					$hightmp->operations[] = $figtmp;
	 					$figtmp = new qtype_correctwriting_operation_change;
	 					$figtmp->operationname = 'red';
	 					$figtmp->position = $work[$i]->posinpicture;
	 					$lowtmp->operations[] = $figtmp;
	 				}
	 				elseif ($work[$i]->operationname == 'changelexem') {
	 					$figtmp = new qtype_correctwriting_operation_deletelexem;
	 					$figtmp->operationname = 'deletelexem';
	 					$hightmp->operations[] = $figtmp;
	 					$figtmp = new qtype_correctwriting_operation_changelexem;
	 					$figtmp->operationname = 'lowline';
	 					$lowtmp->operations[] = $figtmp;
	 				}

	 				$hightmp->symbols .= $sourcestring[$work[$i]->lexeminpicture][$lettercounttype1];
	 				$lettercounttype1 ++;
	 				$lettercountsubblock ++;
	 				$i ++; 			
	 			}
	 		}
 			//if previous loop was entered
 			if ($type1) {
 				$lowtmp->symbols = $resultstring[$lexem];
 				$tmp->subblockhigh[] = $hightmp;
 				$tmp->subblocklow[] = $lowtmp;
 				$lettercountsubblock = 0;
 				$type1 = false;
 				$lowtmp = new qtype_correctwriting_subblock;
 				$hightmp = new qtype_correctwriting_subblock;
 			}

 			//if happaned deleting of this lexem write it to high subblock - low is empty

 			if ($lexem < 0) {
 				if ($work[$i]->operationname == 'deletelexem') {
 					$figtmp = new qtype_correctwriting_operation_deletelexem;
 					$figtmp->operationname = 'deletelexem';
 					$hightmp->operations[] = $figtmp;
 				}
 				elseif ($work[$i]->operationname == 'movelexem') {
 					$figtmp = new qtype_correctwriting_operation_movelexem;
 					$figtmp->operationname = 'movelexemstart';
 					$figtmp->ID = $work[$i]->ID;
 					$hightmp->operations[] = $figtmp;
 				}

 				//generate high subblock for deleted lexem
 				while ($work[$i]->lexem == $lexem && $work[$i]->operationname != 'last') {
 					
 					if ($work[$i]->operationname == 'space') {
 						$i ++;
 						$figtmp = new qtype_correctwriting_operation_space;
 						$figtmp->operationname = 'space';
 						$hightmp->operations[] = $figtmp;
 					}
 					elseif ($work[$i]->operationname == 'insert') {
 						$figtmp = new qtype_correctwriting_operation_insert;
 						$figtmp->operationname = 'insert';
 						$figtmp->position = $lettercountsubblock;
 						while ($work[$i] == 'insert') {
 							$figtmp->symbols .= $work[$i]->symbols;
 							$hightmp->operations[] = $figtmp;
 							$hightmp->symbols .= '  ';
 							$i ++;
 						}
 					}
 					elseif ($work[$i]->operationname == 'dest') {
 						$figtmp = qtype_correctwriting_operation_move;
 						$figtmp->operationname = 'movedest';
 						$figtmp->ID = $work[$i]->ID;
 						$figtmp->position = $lettercountsubblock;
 						$hightmp->operations[] = $figtmp;
 						$i ++;
 					}
 					elseif ($work[$i]->operationname == 'delete') {
 						$figtmp = new qtype_correctwriting_operation_delete;
 						$figtmp->operationname = 'delete'; 
 						$figtmp->position = $lettercountsubblock;
 						$hightmp->operations[] = $figtmp;
 					}
 					elseif ($work[$i]->operationname == 'exchange') {
 						$figtmp = new qtype_correctwriting_operation_exchange;
 						$figtmp->operationname = 'exchange';
 						$figtmp->position = $lettercountsubblock;
 						$hightmp->operations[] = $figtmp;
 					}
 					elseif ($work[$i]->operationname == 'deletespace') {
 						$figtmp = new qtype_correctwriting_operation_deletespace;
 						$figtmp->operationname = 'deletespace';
 						$hightmp->operations[] = $figtmp;
 						$lettercounttype1 = 0;
 						$tmp->subblockhigh[] = $hightmp;
 						$lettercountsubblock = 0;
 					}
 					elseif ($work[$i]->operationname == 'move') {
 						$figtmp = new qtype_correctwriting_operation_move;
 						$figtmp->operationname = 'movestart';
 						$figtmp->ID = $work[$i]->ID;
 						$figtmp->position = $lettercountsubblock;
 						$hightmp->operations[] = $figtmp;
 					}
 					elseif ($work[$i]->operationname == 'change') {
 						$figtmp = new qtype_correctwriting_operation_delete;
 						$figtmp->operationname = 'delete';
 						$figtmp->position = 'lettercountsubblock';
 						$hightmp->operations[] = $figtmp;
 						$figtmp = new qtype_correctwriting_operation_change;
 						$figtmp->operationname = 'red';
 						$figtmp->position = $work[$i]->placeto;
 						$lowtmp->operations[] = $figtmp;
 					}

 					$hightmp->symbols .= $sourcestring[$work[$i]->lexeminpicture][$lettercounttype1];
 					$lettercounttype1 ++;
 					$lettercountsubblock ++;
 					$i ++;

 					if ($work[$i]->operationname == 'movelexem' || $work[$i]->operationname == 'deletelexem')
 						break;
 				}

 				//leave subblock of the lexem empty
 				$lowtmp->symbols = '';
 				//add to block low and high subblocks
 				$tmp->subblockhigh[] = $hightmp;
 				$tmp->subblocklow[] = $lowtmp;
 				$lettercountsubblock = 0;
 				$hightmp = new qtype_correctwriting_subblock;
 				$lowtmp = new qtype_correctwriting_subblock;
 			}

 			//event when block is generated and high subblock is empty
 			if ($work[$i]->operationname == 'insertlexem' && $picturelexem == $work[$i]->lexeminpicture) {

 				if ($work[$i]->ID >0) {
 					$figtmp = new qtype_correctwriting_operation_movelexem;
 					$figtmp->operationname = 'movelexemdest';
 					$figtmp->ID = $work[$i]->ID;
 					$lowtmp->operations[] = $figtmp;
 				}
 				else {
 					$figtmp = new qtype_correctwriting_operation_insertlexem;
 					$figtmp->operationname = 'lowline';
 					$lowtmp->operations[] = $figtmp;
 				}
 				//write index of lexem for operations
 				$lexem = $work[$i]->lexem;

 				//new block is not generated if lexem isn`t move to place which cause of space
 				if ($i > 0 && $work[$i - 1]->operationname == 'space')
 					$type2 = true;

 				while ($work[$i]->lexem == $lexem) {
 					$lowtmp->symbols .= $resultstring[$lexem][$lettercounttype2];
 					$lettercounttype2 ++;
 					$i ++;
 				}
 				//mark the end of inserted lexem for inserting space from the right side
 				$work[$i - 1]->operationname = 'space';

 				//not to generate block (subblock only)
 				if ($type2) {
 					$tmp->subblocklow[] = $lowtmp;
 					$type2 = false;
 					$lettercounttype2 = 0;
 					$lowtmp = new qtype_correctwriting_subblock;
 				}
 				//generate new block
 				else {
 					$tmp->subblocklow[] = $lowtmp;
 					$hightmp->symbols = '';
 					$tmp->subblockhigh[] = $hightmp;
 					$lettercounttype2 = 0;
 					$blocks[] = $tmp;
 					$tmp = new qtype_correctwriting_block;
 					$lowtmp = new qtype_correctwriting_subblock;
 					$wasgeneratedbytype2 = true;
 					break;
 				}
 			}
 			
 		}

 		//if new lexem started on the picture write previous to block
 		if ($picturelexem != $work[$i]->lexeminpicture && !$wasgeneratedbytype2) {
 			$lettercounttype1 = 0;
 			$lettercounttype2 = 0;
 			$blocks[] = $tmp;
 			$tmp = new qtype_correctwriting_block;
 		} 

 	}

 	return $blocks;
}


//this function sets coordinates of subblocks and blocks borders
function qtype_correctwriting_count_words_coordinates ($blocks, & $totalwidth) {

    $blockscount = count($blocks);
    $widthofblock = 0;
    $highwidth = 0;
    $lowwidth = 0;
    $totalwidth = 0;

    //for each block get size of subblocks` text
    //set start coordinates and width of blocks
    for ($i = 0; $i < $blockscount; ++ $i) {
        //start coordinates of block
        if ($i == 0)
            $blocks[$i]->leftcoord = LEFT_BORDER;
        else 
            $blocks[$i]->leftcoord = $blocks[$i -1]->leftcoord + $blocks[$i - 1]->wide + BLOCK_SPACE;

        //subblocks of high string of block
        $highsubcount = count($blocks[$i]->subblockhigh);
        for ($j = 0; $j < $highsubcount; ++ $j) {
            //find width of one subblock
            $subtext = $blocks[$i]->subblockhigh[$j]->symbols;
            $textsize = imagettfbbox(FONT_SIZE, 0, FONT_NAME, $subtext);
            $width = $textsize[2] - $textsize[0];
        
            //deletespace event when we don`t need space between blocks
            if (count($blocks[$i]->subblockhigh[$j]->operations) != 0 && end($blocks[$i]->subblockhigh[$j]->operations)->operationname == 'deletespace') {
                $width += BLOCK_SPACE;
            }
            //save width of subblock and update width of block
            $blocks[$i]->subblockhigh[$j]->wide = $width;
            $highwidth += $width;
        }
        //subblocks of low string
        $lowsubcount = count($blocks[$i]->subblocklow);
        for ($j = 0; $j < $lowsubcount; ++ $j) {
            $subtext = $blocks[$i]->subblocklow[$j]->symbols;
            $textsize = imageftbbox(FONT_SIZE, 0, FONT_NAME, $subtext);
            $width = $textsize[2] - $textsize[0];
            $blocks[$i]->subblocklow[$j]->wide = $width;

            if ($lowsubcount > 1 && (($j + 1) != $lowsubcount))
                //between some low subblocks of the one block add space
                $blocks[$i]->subblocklow[$j]->wide += BLOCK_SPACE;

            $lowwidth += $blocks[$i]->subblocklow[$j]->wide;
        }

        //solve which common width of subblocks to count as width of block
        if ($highwidth > $lowwidth) {
            $blocks[$i]->wide = $highwidth;
            $totalwidth += ($highwidth + BLOCK_SPACE);
            //set start coordiates of subblocks  of current block for current event
            for ($h = 0; $h < $highsubcount; ++ $h) {
                if ($h == 0)
                    $blocks[$i]->subblockhigh[$h]->leftcoord = $blocks[$i]->leftcoord;
                else
                    $blocks[$i]->subblockhigh[$h]->leftcoord = $blocks[$i]->subblockhigh[$h - 1]->leftcoord + $blocks[$i]->subblockhigh[$h - 1]->wide;
            }
            //low subblocks are in the middle
            for ($l = 0; $l < $lowsubcount; ++ $l) {
                if ($l == 0)
                    $blocks[$i]->subblocklow[$l]->leftcoord = $blocks[$i]->leftcoord + ($highwidth - $lowwidth) / 2;
                else
                    $blocks[$i]->subblocklow[$l]->leftcoord = $blocks[$i]->subblocklow[$l - 1]->leftcoord + $blocks[$i]->subblocklow[$l - 1]->wide;
            }
        }
        else {
            $blocks[$i]->wide = $lowwidth;
            $totalwidth += ($lowwidth + BLOCK_SPACE);
            //low subblocks across the whole block
            for ($l = 0; $l < $lowsubcount; ++ $l) {
                if ($l == 0)
                    $blocks[$i]->subblocklow[$l]->leftcoord = $blocks[$i]->leftcoord;
                else
                    $blocks[$i]->subblocklow[$l]->leftcoord = $blocks[$i]->subblocklow[$l - 1]->leftcoord + $blocks[$i]->subblocklow[$l -1]->wide;
            }
            //high subblocks are in the middle
            for ($h = 0; $h < $highsubcount; ++ $h) {
                if ($h == 0)
                    $blocks[$i]->subblockhigh[$h]->leftcoord = $blocks[$i]->leftcoord + ($lowwidth - $highwidth) / 2;
                else
                    $blocks[$i]->subblockhigh[$h]->leftcoord = $blocks[$i]->subblockhigh[$h - 1]->leftcoord + $blocks[$i]->subblockhigh[$h - 1]->wide;
            }
        }

        $highwidth = 0;
        $lowwidth = 0;
    }
    return $blocks;
}

//A function to set coordinates of operations` figures
function qtype_correctwriting_count_operations_coordinates ($blocks) {
    
    $blockscount = count($blocks);
    //for each block
    for ($i = 0; $i < $blockscount; ++ $i) {
        $shift = 0;//a counter for spaces from insert operation
        $highsubcount = count($blocks[$i]->subblockhigh);
        //for each high subblock
        for ($h =0; $h < $highsubcount; ++ $h) {
            $opercount = count($blocks[$i]->subblockhigh[$h]->operations);
            //for each operation
            for ($op = 0; $op < $opercount; ++ $op) {
                $blocks[$i]->subblockhigh[$h]->operations[$op]->count_coordinates ($blocks, $shift, $i, $h, $op);
            }
        }
        $lowsubcount = count($blocks[$i]->subblocklow);
        //for each low subblock
        for ($l =0; $l < $lowsubcount; ++ $l) {
            $opercount = count($blocks[$i]->subblocklow[$l]->operations);
            //for each operation
            for ($op = 0; $op < $opercount; ++ $op) {
                $blocks[$i]->subblocklow[$l]->operations[$op]->count_coordinates ($blocks, $shift, $i, $l, $op);
            }
        }
    }
    return $blocks;
}

//A function to draw picture
function qtype_correctwriting_draw_picture ($lexems, $stringoperations) {

    $source = $lexems;
    $objectoperations = qtype_correctwriting_get_objects($stringoperations);
    $lexems = qtype_correctwriting_get_result_string ($lexems, $objectoperations);
    $work = qtype_correctwriting_recount_indexes ($objectoperations, $source);
    $blocks = qtype_correctwriting_generate_blocks ($work, $source, $lexems);
    $blocks = qtype_correctwriting_count_words_coordinates ($blocks, $totalwidth);

    $image = imagecreatetruecolor($totalwidth, 400);
    imagesetthickness($image, 2);
    imageantialias($image, true);
    imagefill($image, 0, 0, 0xEEEEFF);

    foreach ($blocks as $block) {
        $block->draw($image);
    }

    $blocks = qtype_correctwriting_count_operations_coordinates ($blocks);
        
    foreach ($blocks as $block) {
       foreach ($block->subblockhigh as $high) {
           $opercount = count($high->operations);
           for ($i = 0; $i < $opercount; ++ $i) {
               $high->operations[$i]->draw($image);
           }
       }
       foreach ($block->subblocklow as $low) {
           $opercount = count($low->operations);
            for ($i = 0; $i < $opercount; ++ $i) {
                $low->operations[$i]->draw($image);
            }
        }
    }
       
    imagepng($image, '/home/user/Desktop/123.png');
}

